#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0400
#endif

#include "pvm/pvmapi.h"

#include <windows.h>
#include "lib/pvmsystem.h"

#include "lib/memory/pvmmemory.h"

#if defined(_DEBUG) && defined(USE_CRITICAL_WATCH)

CriticalWatch::CriticalWatch(PVMCriticalSection* c)
{
	target = c;
}

CriticalWatch::~CriticalWatch()
{
	if(target!=NULL)
	{
		MessageBox(NULL, L"Watch error!", L"Watcher!!!", MB_OK);
	}
}

void CriticalWatch::Remove(PVMCriticalSection* c)
{
	target = NULL;
}
#endif

PVMCriticalSection* PVMGetCriticalForMmgr()
{
	return NULL;
}

void PVMFreeCriticalForMmgr()
{
}

pvm_bool PVMIsThreadSyncAvailable()
{
	return true;
}

pvm_bool PVMIsThreadsAvailable()
{
	return true;
}

pvm_bool PVMInitCritical(PVMCriticalSection *critical)
{
#if defined(_DEBUG) && defined(DEBUG_CRITICAL_SECTIONS)
	InitializeCriticalSection(&critical->section);
#else
	InitializeCriticalSection(critical);
#endif
	return true;
}

void PVMDeinitCritical(PVMCriticalSection *critical)
{
#if defined(_DEBUG) && defined(DEBUG_CRITICAL_SECTIONS)
	DeleteCriticalSection(&critical->section);
#else
	DeleteCriticalSection(critical);
#endif
}

#if defined(_DEBUG) && defined(DEBUG_CRITICAL_SECTIONS)
pvm_bool PVMEnterCriticalD(PVMCriticalSection *critical, char* file, int line)
{
	BOOL tryRes;
	DWORD startTime, curTime;

	startTime = GetTickCount();
	while(TryEnterCriticalSection(&critical->section)==FALSE)
	{
		curTime = GetTickCount();;
		if(curTime - startTime>1500)
		{
			int i = 0;
		}
	}

	strcpy(critical->lastLockFile, file);
	critical->lastLockLine = line;

	return true;
}

void PVMLeaveCriticalD(PVMCriticalSection *critical)
{
	LeaveCriticalSection(&critical->section);
}

#else

pvm_bool PVMEnterCritical(PVMCriticalSection *critical)
{
	EnterCriticalSection(critical);
	return true;
}

void PVMLeaveCritical(PVMCriticalSection *critical)
{
	LeaveCriticalSection(critical);
}

#endif

pvm_bool PVMInitEvent(PVMEventHandle *event)
{
	if(event==NULL)
		return false;
	*event = CreateEvent(NULL, FALSE, FALSE, NULL); 
	if(*event==NULL)
		return false;

	return true;
}

void PVMDeinitEvent(PVMEventHandle *event)
{
	if(event==NULL || *event==NULL)
		return;

	CloseHandle(*event);
}

pvm_bool PVMWaitForEvent(PVMEventHandle *event)
{
	if(WaitForSingleObject(*event, INFINITE)==WAIT_OBJECT_0)
		return true;

	return false;
}

void PVMSignalEvent(PVMEventHandle *event)
{
	if(event==NULL || *event==NULL)
		return;
	SetEvent(*event);
}

void PVMInitThreadVar(PVMThreadHandle* outHandle)
{
	*outHandle = NULL;
}

pvm_bool PVMIsThreadValid(PVMThreadHandle threadHandle)
{
	return threadHandle!=NULL;
}

PVMThreadHandle PVMGetCurrentThread()
{
	return GetCurrentThread();
}

pvm_bool PVMCreateThread(PVMThreadHandle* outHandle, PVMThreadStartRoutine routine, void *param)
{
	DWORD dwPlayThreadId = 0;
	if(!outHandle)
		return false;
	DWORD dwCreationFlags = 0;

	*outHandle = CreateThread(NULL, 16384, (LPTHREAD_START_ROUTINE)routine, param, dwCreationFlags, &dwPlayThreadId); 
	if(!*outHandle)
		return false;
	return true;
}

void PVMSuspendThread(PVMThreadHandle threadHandle)
{
	SuspendThread(threadHandle);
}

void PVMResumeThread(PVMThreadHandle threadHandle)
{
	ResumeThread(threadHandle);
}

int PVMJoinThread(PVMThreadHandle threadHandle)
{
	if(WaitForSingleObject(threadHandle, INFINITE)==WAIT_FAILED)
		return WAIT_FAILED;
	return 0;
}

void PVMSleep(pvm_uint32 sleepTime)
{
	Sleep(sleepTime);
}

pvm_uint32 PVMGetMilliSecondsCount()
{
	return GetTickCount();
}

#if defined(_USRDLL)
//////////////////////////////////////////////////////////////////////////
//Function realizations:
BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved)
{
    return TRUE;
}

#endif

PVMFileHandle PVMFileOpen(const pvm_char* fileName, pvm_int32 mode)
{
	DWORD desiredAccess = 0;
	DWORD shareMode = FILE_SHARE_READ;
	DWORD createDisposition = 0;

	if(mode&PVM_READ)
		desiredAccess |= GENERIC_READ;
	if(mode&PVM_WRITE)
		desiredAccess |= GENERIC_WRITE;

	if(mode&PVM_CREATE_ALWAYS)
		createDisposition |= CREATE_ALWAYS;
	if(mode&PVM_OPEN_EXISTING)
		createDisposition |= OPEN_EXISTING;
	if(mode&PVM_OPEN_ALWAYS)
		createDisposition |= OPEN_ALWAYS;

	return CreateFile((LPCWSTR)fileName, desiredAccess, shareMode, NULL, createDisposition, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL);
}

void PVMFileClose(PVMFileHandle file)
{
	CloseHandle(file);
}

pvm_bool PVMFileRead(PVMFileHandle file, void* buffer, pvm_size nBytes, pvm_size* readResult)
{
	if(readResult==NULL)
		return false;
	*readResult = 0;
	DWORD lReadRes = 0;
	pvm_bool retRes = ReadFile(file, buffer, nBytes, &lReadRes, NULL);
	*readResult = lReadRes;
	return retRes;
}

pvm_bool PVMFileWrite(PVMFileHandle file, void* buffer, pvm_size nBytes, pvm_size* writeResult)
{
	if(writeResult==NULL)
		return false;
	*writeResult = 0;
	DWORD lWriteRes = 0;
	pvm_bool retRes = WriteFile(file, buffer, nBytes, &lWriteRes, NULL);
	*writeResult = lWriteRes;
	return retRes;
}

pvm_file_size PVMFileSize(PVMFileHandle file)
{
	if(sizeof(pvm_file_size)>=8)
	{
		DWORD sizeLo = 0;
		DWORD sizeHi = 0;
		DWORD dwError = 0;

		sizeLo = GetFileSize(file, &sizeHi);

		if(sizeLo == 0xFFFFFFFF && (dwError = GetLastError()) != NO_ERROR)
			return PVM_INVALID_FILE_SIZE;


		pvm_file_size fileSize = 0;

		fileSize = sizeHi;
		fileSize <<= 4;
		fileSize |= sizeLo;

		return fileSize;
	}
	else
	{
		return GetFileSize(file, NULL);
	}
}

pvm_bool PVMFileSeek(PVMFileHandle file, pvm_file_size pos, pvm_int32 origin)
{
	 DWORD moveMethod = 0;
	 DWORD dwPtr = 0;
	 DWORD dwError = 0;

	 switch(origin)
	 {
	 case EPVM_SEEK_BEGIN:
		moveMethod = FILE_BEGIN;
		break;
	 case EPVM_SEEK_CURRENT:
		moveMethod = FILE_CURRENT;
		break;
	 case EPVM_SEEK_END:
		moveMethod = FILE_END;
		break;
	 };


	if(sizeof(pvm_file_size)>=8)
	{
		LONG	dwPosHi;
		LONG	dwPosLo;

		dwPosLo = (LONG)(pos & 0xFFFFFFFF);
		dwPosHi = (LONG)((pos >> 32) & 0xFFFFFFFF);
		dwPtr = SetFilePointer(file, dwPosLo, &dwPosHi, moveMethod); 
	}
	else
	{
		dwPtr = SetFilePointer(file, (LONG)pos, NULL, moveMethod); 
	}

	if(dwPtr == 0xFFFFFFFF && (dwError = GetLastError()) != NO_ERROR)
		return false;
	else
		return true;
}

pvm_file_size PVMFileTell(PVMFileHandle file)
{
	 DWORD dwPtr = 0;
	 DWORD dwError = 0;
	 pvm_file_size fileSize = 0;

	if(sizeof(pvm_file_size)>=8)
	{
		LONG	dwPosHi = 0;
		LONG	dwPosLo = 0;

		dwPtr = SetFilePointer(file, dwPosLo, &dwPosHi, FILE_CURRENT); 
		fileSize = dwPosHi;
		fileSize <<= 32;
		fileSize |= dwPtr;
	}
	else
	{
		dwPtr = SetFilePointer(file, 0, NULL, FILE_CURRENT); 
		fileSize = dwPtr;
	}

	if(dwPtr == 0xFFFFFFFF && (dwError = GetLastError()) != NO_ERROR)
		return PVM_INVALID_FILE_SIZE;
	else
		return fileSize;
}

pvm_bool PVMSystemInit()
{
	return true;
}

void PVMSystemDeinit()
{
}

void PVMSysLogOutString(const pvm_char* logString)
{
	OutputDebugStringW((LPCWSTR)logString);
	OutputDebugStringW(L"\r\n");
}